package xyz.scootaloo.kami.server.service

import xyz.scootaloo.kami.server.model.AccessType

/**
 * @author flutterdash@qq.com
 * @since 2022/2/10 22:45
 */
class SentryNode(
    val itemKey: String,
    var rule: ByteArray,
    val parent: SentryNode?,
    var valid: Boolean,
    private var capacityLimit: Long,
    private var capacityAvailable: Long,
    var created: Long,
    var updated: Long,
    val subItems: HashMap<String, SentryNode>
) : RuleSystemService.RuleItem {

    override fun containsSubItem(itemKey: String): Boolean = subItems.containsKey(itemKey)
    override fun subItem(itemKey: String): RuleSystemService.LowRuleItem = subItems[itemKey]!!
    override fun created(): Long = created
    override fun updated(): Long = updated
    override fun rule(): ByteArray = rule.clone()
    override fun hasRuleRestrict(): Boolean = rule.any { it > 0 }
    override fun hasCapacityRestrict(): Boolean = (capacityLimit > 0)
    override fun capacityLimit(): Long = capacityLimit
    override fun capacityAvailable(): Long = capacityAvailable
    override fun isAllow(userLevel: Int, type: AccessType): Boolean {
        return VirtualFileSystem.Checker.isAllow(userLevel, type, rule)
    }

    override fun fullPath(): String {
        return buildChain().joinToString("/")
    }

    fun deleteSubItem(key: String) {
        subItems.remove(key)
    }

    fun buildChain(): List<String> {
        val chain = ArrayList<String>()
        var currentNode = this

        while (currentNode.parent != null) {
            chain.add(currentNode.itemKey)
            currentNode = currentNode.parent!!
        }

        if (chain.size > 1)
            chain.reverse()
        return chain
    }

    fun ruleInvalidate() {
        for (idx in rule.indices)
            rule[idx] = 0
        if (capacityLimit < 0 && capacityAvailable < 0)
            valid = false
    }

    fun limitInvalidate() {
        capacityLimit = -1
        capacityAvailable = -1
        for (idx in rule.indices)
            if (rule[idx] > 0)
                return
        valid = false
    }

    fun updateRule(newRule: ByteArray) {
        for (idx in rule.indices) {
            rule[idx] = newRule[idx]
            if (rule[idx] != ZERO_BYTE)
                valid = true
        }
    }

    fun updateCreateInfo(created: Long, updated: Long) {
        if (this.created > created)
            this.created = created
        if (this.updated < updated)
            this.updated = updated
    }

    fun cutCapAvailable(amount: Long) {
        capacityAvailable -= amount
    }

    fun releaseCapAvailable(amount: Long) {
        capacityAvailable += amount
    }

    fun updateCapAvailableByUsed(used: Long) {
        capacityAvailable = capacityLimit - used
    }

    fun updateCapLimit(newLimit: Long) {
        if (newLimit > ZERO_LONG) {
            capacityLimit = newLimit
            valid = true
        }
    }

    fun copy(): SentryNode {
        return SentryNode(
            this.itemKey, this.rule, this.parent, false,
            this.capacityLimit, this.capacityAvailable,
            this.created, this.updated, this.subItems
        )
    }

    fun copyWithRule(): SentryNode {
        return SentryNode(
            this.itemKey, this.rule.copyOf(), this.parent, false,
            this.capacityLimit, this.capacityAvailable,
            this.created, this.updated, HashMap(0)
        )
    }

    companion object {
        const val ZERO_BYTE = 0.toByte()
        const val ZERO_LONG = 0.toLong()

        val FAKE_NODE by lazy { createEmptyNode("", null, false) }

        private val EMPTY_RULE: ByteArray get() = byteArrayOf(0, 0, 0, 0)

        fun createEmptyNode(
            pathItem: String, parent: SentryNode?, valid: Boolean = false
        ) = SentryNode(
            pathItem, EMPTY_RULE, parent, valid,
            -1, -1,
            0, 0, HashMap(2)
        )
    }
}